import Util from "@app/shared/models/Util";
import {Direction, SortDirection, SortOrder} from "@app/shared/models/page/SortOrder";
import {emptyPageData, PageData} from "@app/shared/models/page/PageData";

export const MAX_SAFE_PAGE_SIZE = 2147483647;

export type PageLinkSearchFunction<T> = (entity: T, textSearch: string, searchProperty?: string) => boolean;


/**
 * 创建一个默认的分页链接搜索函数。
 *
 * @param searchProperty 可选参数，指定要在实体上进行搜索的属性名称。
 * @returns 返回一个函数，该函数接收两个参数（实体和文本搜索字符串），并返回一个经过搜索处理的分页链接。
 */
export const defaultPageLinkSearchFunction = (searchProperty?: string): PageLinkSearchFunction<any> => {
  // 返回一个闭包函数，将外部的searchProperty传递给defaultPageLinkSearch函数
  return (entity, textSearch) => defaultPageLinkSearch(entity, textSearch, searchProperty);
}

/**
 * 默认的页面链接搜索函数，用于在给定实体中搜索指定的文本。
 * @param entity 要搜索的实体对象。
 * @param textSearch 要在实体中搜索的文本。
 * @param searchProperty 指定在实体的哪个属性中进行搜索。如果未指定，则搜索实体的所有属性。
 * @returns {boolean} 如果找到匹配的文本，则返回 true；否则返回 false。
 */
const defaultPageLinkSearch: PageLinkSearchFunction<any> =
  (entity: any, textSearch: string, searchProperty?: string): boolean => {
    // 如果文本搜索为空或长度为零，则直接返回 true
    if (textSearch === null || !textSearch.length) {
      return true;
    }
    // 将文本搜索转换为小写，以便进行不区分大小写的比较
    const expected = ('' + textSearch).toLowerCase();
    // 如果指定了搜索属性
    if (searchProperty && searchProperty.length) {
      // 检查实体是否具有指定的搜索属性
      if (Object.prototype.hasOwnProperty.call(entity, searchProperty)) {
        const val = entity[searchProperty];
        // 如果属性值不为 null，并且不是对象类型，则进行字符串比较
        if (val !== null) {
          if (val !== Object(val)) {
            const actual = ('' + val).toLowerCase();
            // 如果属性值中包含搜索的文本，则返回 true
            if (actual.indexOf(expected) !== -1) {
              return true;
            }
          }
        }
      }
    } else {
      // 如果没有指定搜索属性，则遍历实体的所有属性
      for (const key of Object.keys(entity)) {
        const val = entity[key];
        // 如果属性值不为 null，并且不是对象类型，则进行字符串比较
        if (val !== null) {
          if (val !== Object(val)) {
            const actual = ('' + val).toLowerCase();
            // 如果属性值中包含搜索的文本，则返回 true
            if (actual.indexOf(expected) !== -1) {
              return true;
            }
          } else if (Util.isObject(val)) {
            // 如果属性值是对象，则递归调用搜索函数进行深度搜索
            if (defaultPageLinkSearch(val, textSearch)) {
              return true;
            }
          }
        }
      }
    }
    // 如果遍历完所有属性都没有找到匹配的文本，则返回 false
    return false;
  };

/**
 * 对两个项目进行排序的函数。
 * @param item1 第一个要比较的项。
 * @param item2 第二个要比较的项。
 * @param property 要用来比较的项的属性路径。
 * @param asc 是否按升序排序。true 表示升序，false 表示降序。
 * @returns 返回一个数字，根据指定的属性和排序顺序，指示 item1 和 item2 的相对顺序。
 */
export const sortItems = (item1: any, item2: any, property: string, asc: boolean): number => {
  // 获取 item1 和 item2 指定属性的值
  const item1Value = Util.getDescendantProp(item1, property);
  const item2Value = Util.getDescendantProp(item2, property);
  let result = 0; // 初始化结果为 0，表示相等

  // 如果两个项的属性值不相等，则进行比较
  if (item1Value !== item2Value) {
    const item1Type = typeof item1Value; // 获取 item1 值的类型
    const item2Type = typeof item2Value; // 获取 item2 值的类型

    // 数字类型的比较
    if (item1Type === 'number' && item2Type === 'number') {
      result = item1Value - item2Value;
      // 字符串类型的比较
    } else if (item1Type === 'string' && item2Type === 'string') {
      result = item1Value.localeCompare(item2Value);
      // 布尔类型或类型不一致的比较
    } else if ((item1Type === 'boolean' && item2Type === 'boolean') || (item1Type !== item2Type)) {
      if (item1Value && !item2Value) {
        result = 1; // item1 值为真，item2 值为假，则 item1 在前
      } else if (!item1Value && item2Value) {
        result = -1; // item1 值为假，item2 值为真，则 item2 在前
      }
    }
  }

  // 根据升序或降序的要求，调整结果的正负
  return asc ? result : result * -1;
};


export class PageLink {

  textSearch: string;
  pageSize: number;
  page: number;
  sortOrder: SortOrder;

  constructor(pageSize: number, page: number = 0, textSearch: string | null = null, sortOrder: SortOrder | null = null) {
    this.textSearch = textSearch as string;
    this.pageSize = pageSize;
    this.page = page;
    this.sortOrder = sortOrder as SortOrder;
  }

  public nextPageLink(): PageLink {
    return new PageLink(this.pageSize, this.page + 1, this.textSearch, this.sortOrder);
  }

  /**
   * 构建查询字符串
   * 该方法基于当前实例的属性（pageSize，page，textSearch，sortOrder），构建并返回一个用于请求的查询字符串。
   *
   * @returns {string} 返回构建好的查询字符串。
   */
  public toQuery(): string {
    // 初始化查询字符串，包含pageSize和page两个基本参数
    let query = `?pageSize=${this.pageSize}&page=${this.page}`;
    // 处理文本搜索参数
    const textSearchParams = this.textSearch?.trim();
    if (textSearchParams?.length) {
      const textSearch = encodeURIComponent(textSearchParams);
      // 如果存在文本搜索参数，则将其添加到查询字符串中
      query += `&textSearch=${textSearch}`;
    }
    // 处理排序参数
    if (this.sortOrder) {
      // 如果存在排序参数，则将其添加到查询字符串中
      query += `&sortProperty=${this.sortOrder.property}&sortOrder=${this.sortOrder.direction}`;
    }
    return query;
  }

  public sort(item1: any, item2: any): number {
    if (this.sortOrder) {
      const sortProperty = this.sortOrder.property;
      const asc = this.sortOrder.direction === Direction.ASC;
      return sortItems(item1, item2, sortProperty, asc);
    }
    return 0;
  }

  public filterData<T>(data: Array<T>,
                       searchFunction: PageLinkSearchFunction<T> = defaultPageLinkSearchFunction()): PageData<T> {
    const pageData = emptyPageData<T>();
    pageData.data = [...data];
    if (this.textSearch && this.textSearch.length) {
      pageData.data = pageData.data.filter((entity) => searchFunction(entity, this.textSearch));
    }
    pageData.totalElements = pageData.data.length;
    pageData.totalPages = this.pageSize === Number.POSITIVE_INFINITY ? 1 : Math.ceil(pageData.totalElements / this.pageSize);
    if (this.sortOrder) {
      const sortProperty = this.sortOrder.property;
      const asc = this.sortOrder.direction === Direction.ASC;
      pageData.data = pageData.data.sort((a, b) => sortItems(a, b, sortProperty, asc));
    }
    if (this.pageSize !== Number.POSITIVE_INFINITY) {
      const startIndex = this.pageSize * this.page;
      pageData.data = pageData.data.slice(startIndex, startIndex + this.pageSize);
      pageData.hasNext = pageData.totalElements > startIndex + pageData.data.length;
    }
    return pageData;
  }

  public sortDirection(): SortDirection {
    if (this.sortOrder) {
      return (this.sortOrder.direction + '').toLowerCase() as SortDirection;
    } else {
      return '';
    }
  }

}

